// ============================================================================
// FileName: SIPProxyRuntimeConfig.cs
//
// Description:
// Base class for the dynamic runtime configuration files.
//
// Author(s):
// Aaron Clauson
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using mimosa.SIP;
using mimosa.Net;
using log4net;

#if UNITTEST
using NUnit.Framework;
#endif

namespace mimosa.SIPServer
{
	public class SIPProxyRuntimeConfig
	{	
		private const string ANON_CALLERS = @"anonymous\.invalid|anonymous|anon";

		private static ILog logger = log4net.LogManager.GetLogger("sipproxy");
		
		public DateTime Created = DateTime.Now;

		public SIPDialPlan DialPlan;

		public SIPProxyRuntimeConfig()
		{}

		public SIPProxyRuntimeConfig(string dialPlan)
		{
			try
			{
				DialPlan = new SIPDialPlan(dialPlan);
			}
			catch(Exception excp)
			{
				logger.Error("Exception SIPProxyRuntimeConfig. " + excp.Message);
				throw excp;
			}
		}

        /// <summary>
        /// Decides on what switch call command should be used for an incoming call request. The decision is made by 
        /// attempting to locate a match in the user's dial plan.
        /// </summary>
        /// <param name="localEndPoint">The socket on the proxy that the call request was received on.</param>
        /// <param name="remoteEndPoint">The socket the call request was received from.</param>
        /// <param name="sipRequest">The received call request.</param>
        /// <returns>A struct inidcating where and how the call should be forwarded on.</returns>
		public virtual SwitchCallStruct SwitchCallDialPlan(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SIPRequest sipRequest)
		{
            try
            {
                SwitchCallStruct call = SwitchCall(localEndPoint, remoteEndPoint, sipRequest);

                if (call != SwitchCallStruct.Empty)
                {
                    return call;
                }
                else if (DialPlan != null && DialPlan.Commands.Length > 0)
                {

                    bool match = false;
                    SIPSwitchCommand swCommand = null;

                    #region Attempt to find a mathcing dial plan command for the call.

                    foreach (SIPDialPlanCommand dialPlanCommand in DialPlan.Commands)
                    {
                        switch (dialPlanCommand.Operation)
                        {
                            case SIPDialPlanOpsEnum.Equals:
                                // Two modes on the equals match, if pattern starts with an underscore use Asterisk compatible mode.

                                if (dialPlanCommand.Destination.StartsWith("_"))
                                {
                                    // X = \d.
                                    // Z = [1-9]
                                    // N = [2-9]
                                    string astPattern = dialPlanCommand.Destination.Substring(1);
                                    string dst = sipRequest.URI.User;
                                    int dstCharIndex = 0;
                                    int astCharIndex = 0;

                                    /*astPattern = astPattern.Replace("*", @"\*");
                                    astPattern = Regex.Replace(astPattern, "x", @"\d", RegexOptions.IgnoreCase);
                                    astPattern = Regex.Replace(astPattern, "z", "[1-9]", RegexOptions.IgnoreCase);
                                    astPattern = Regex.Replace(astPattern, "n", "[2-9]", RegexOptions.IgnoreCase);

                                    match = Regex.Match(sipRequest.URI.User, astPattern).Success;*/

                                    while (dst != null && dst.Length > dstCharIndex && astPattern != null && astPattern.Length > astCharIndex)
                                    {
                                        char astChar = astPattern.ToCharArray()[astCharIndex];
                                        string dstSubStr = dst.Substring(dstCharIndex, 1);
                                        if (astCharIndex + 1 < astPattern.Length)
                                        {
                                            if (astPattern.ToCharArray()[astCharIndex + 1] == '.')
                                            {
                                                dstSubStr = dst.Substring(dstCharIndex);
                                                astCharIndex++;
                                                dstCharIndex = dst.Length - 1;
                                            }
                                        }

                                        if (astChar == '.')
                                        {
                                            match = true;
                                            break;
                                        }
                                        else if (astChar == 'x' || astChar == 'X')
                                        {
                                            if (Regex.Match(dstSubStr, @"[^\d]").Success)
                                            {
                                                break;
                                            }
                                        }
                                        else if (astChar == 'z' || astChar == 'Z')
                                        {
                                            if (Regex.Match(dstSubStr, "[^1-9]").Success)
                                            {
                                                break;
                                            }
                                        }
                                        else if (astChar == 'n' || astChar == 'N')
                                        {
                                            if (Regex.Match(dstSubStr, "[^2-9]").Success)
                                            {
                                                break;
                                            }
                                        }
                                        else if (astChar.ToString() != dstSubStr)
                                        {
                                            break;
                                        }

                                        if (dstCharIndex == dst.Length - 1 && astCharIndex == astPattern.Length - 1)
                                        {
                                            match = true;
                                            break;
                                        }
                                        else
                                        {
                                            dstCharIndex++;
                                            astCharIndex++;
                                        }
                                    }
                                }
                                else
                                {
                                    match = (dialPlanCommand.Destination == sipRequest.URI.User);
                                }
                                break;
                            case SIPDialPlanOpsEnum.Regex:
                                match = (Regex.Match(sipRequest.URI.User, dialPlanCommand.Destination).Success);
                                break;
                            default:
                                // No match.
                                break;
                        }

                        if (match)
                        {
                            swCommand = dialPlanCommand.SwitchCommand;
                            break;
                        }
                    }

                    #endregion

                    if (match && swCommand != null)
                    {
                        string forwardURIStr = swCommand.Forward;
                        if (!forwardURIStr.StartsWith("sip:"))
                        {
                            forwardURIStr = "sip:" + forwardURIStr;
                        }

                        SIPURI forwardURI = SIPURI.ParseSIPURI(forwardURIStr);

                        if (Regex.Match(forwardURI.User, @"\$\{(dst|exten):\d+\}", RegexOptions.IgnoreCase).Success)
                        {
                            int trimCharCount = Convert.ToInt32(Regex.Match(forwardURI.User, @"\$\{(dst|exten):(?<count>\d+)\}", RegexOptions.IgnoreCase).Result("${count}"));

                            if (trimCharCount < sipRequest.URI.User.Length)
                            {
                                string trimmedDst = sipRequest.URI.User.Substring(trimCharCount);
                                forwardURI.User = Regex.Replace(forwardURI.User, @"\$\{dst\}|\$\{exten.*?\}", trimmedDst, RegexOptions.IgnoreCase);
                            }
                            else
                            {
                                logger.Warn("A SIP destination could not be trimmed " + forwardURI.User + " " + sipRequest.URI.User + ", original destination being used.");
                                forwardURI.User = sipRequest.URI.User;
                            }
                        }
                        else
                        {
                            forwardURI.User = Regex.Replace(forwardURI.User, @"\$\{dst\}|\$\{exten\}", sipRequest.URI.User, RegexOptions.IgnoreCase);
                        }

                        string fromHeaderStr = null;
                        if (swCommand.FromHeader != null && swCommand.FromHeader.Trim().Length > 0)
                        {
                            SIPHeaderParserError fromParserError = SIPHeaderParserError.None;
                            SIPFromHeader dialplanFrom = SIPFromHeader.ParseFromHeader(swCommand.FromHeader, out fromParserError);
                            if (fromParserError != SIPHeaderParserError.None)
                            {
                                throw new ApplicationException("A dial plan From header value could not be parsed " + fromParserError + ". Value=" + fromParserError + ".");
                            }
                            else
                            {
                                fromHeaderStr = swCommand.FromHeader;
                            }
                        }
                        else if (Regex.Match(swCommand.Username, ANON_CALLERS).Success)
                        {
                            fromHeaderStr = "sip:" + swCommand.Username + "@" + sipRequest.Header.From.FromURI.Host;
                        }
                        else
                        {
                            fromHeaderStr = "sip:" + swCommand.Username + "@" + forwardURI.Host;
                        }

                        // Set the sendto socket for the forwarded call.
                        string sendToSocket = null;
                        if (swCommand.SendToSocket != null && swCommand.SendToSocket.Trim().Length > 0)
                        {
                            IPEndPoint dialplanSocket = IPSocket.GetIPEndPoint(swCommand.SendToSocket);
                            if (dialplanSocket.Port == 0)
                            {
                                dialplanSocket.Port = SIPConstants.DEFAULT_SIP_PORT;
                            }
                            sendToSocket = dialplanSocket.ToString();
                        }
                        else
                        {
                            sendToSocket = forwardURI.Host;
                        }

                        //call = new SwitchCallStruct(swCommand.Username, swCommand.Password, forwardURI.ToString(), from, forwardURI.ToString(), forwardURI.Host);
                        call = new SwitchCallStruct(swCommand.Username, swCommand.Password, forwardURI.ToString(), fromHeaderStr, forwardURI.ToString(), sendToSocket, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second, true, true);

                        return call;
                    }
                }

                return call;
            }
            catch (Exception excp)
            {
                logger.Error("Exception SwitchCallDialPlan. " + excp.Message);
                //return SwitchCallStruct.Empty;
                throw excp;
            }
		}
	
		public virtual SwitchCallStruct SwitchCall(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SIPRequest sipRequest)
		{
			return SwitchCallStruct.Empty;
		}

		#region Unit testing.

		#if UNITTEST

		[TestFixture]
		public class SIPProxyRuntimeConfigUnitTest
		{			
			[TestFixtureSetUp]
			public void Init()
			{	}

			[TestFixtureTearDown]
			public void Dispose()
			{			
				
			}

			[Test]
			public void SampleTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				Assert.IsTrue(true, "True was false.");

				Console.WriteLine("---------------------------------"); 
			}	
			
			[Test]
			public void SimpleMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:101@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 100,1,Switch(""anonymous.invalid"", ""password"", ""612@freeworlddialup.com"")
					exten => 101,1,Switch(""username"", ""password"", ""303@sip.blueface.ie"")
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Username == "username", "The dial plan resulted in an incorrect next call username, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Password == "password", "The dial plan resulted in an incorrect next call password, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Uri == "sip:303@sip.blueface.ie", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Assert.IsTrue(nextCall.From == "sip:username@sip.blueface.ie", "The dial plan resulted in an incorrect next call from string, " + nextCall.From + ".");
				Assert.IsTrue(nextCall.To == "sip:303@sip.blueface.ie", "The dial plan resulted in an incorrect next call to string, " + nextCall.To + ".");
				Assert.IsTrue(nextCall.Socket == "sip.blueface.ie", "The dial plan resulted in an incorrect next call socket, " + nextCall.Socket + ".");

				Console.WriteLine("---------------------------------"); 
			}

			
			[Test]
			public void PatternInURIMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:100@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 100,1,Switch(""anonymous.invalid"", ""password"", ""${dst}@freeworlddialup.com"")
					exten => 101,1,Switch(""username"", ""password"", ""303@sip.blueface.ie"")
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Username == "anonymous.invalid", "The dial plan resulted in an incorrect next call username, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Password == "password", "The dial plan resulted in an incorrect next call password, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Uri == "sip:100@freeworlddialup.com", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Assert.IsTrue(nextCall.From == "sip:anonymous.invalid@sip.CNSIPSwitch.com", "The dial plan resulted in an incorrect next call from string, " + nextCall.From + ".");
				Assert.IsTrue(nextCall.To == "sip:100@freeworlddialup.com", "The dial plan resulted in an incorrect next call to string, " + nextCall.To + ".");
				Assert.IsTrue(nextCall.Socket == "freeworlddialup.com", "The dial plan resulted in an incorrect next call socket, " + nextCall.Socket + ".");

				Console.WriteLine("---------------------------------"); 
			}

			[Test]
			public void SubstringPatternInURIMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:100123@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 100123,1,Switch(""anonymous.invalid"", ""password"", ""${dst:2}@freeworlddialup.com"")
					exten => 101,1,Switch(""username"", ""password"", ""303@sip.blueface.ie"")
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Username == "anonymous.invalid", "The dial plan resulted in an incorrect next call username, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Password == "password", "The dial plan resulted in an incorrect next call password, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Uri == "sip:0123@freeworlddialup.com", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Assert.IsTrue(nextCall.From == "sip:anonymous.invalid@sip.CNSIPSwitch.com", "The dial plan resulted in an incorrect next call from string, " + nextCall.From + ".");
				Assert.IsTrue(nextCall.To == "sip:0123@freeworlddialup.com", "The dial plan resulted in an incorrect next call to string, " + nextCall.To + ".");
				Assert.IsTrue(nextCall.Socket == "freeworlddialup.com", "The dial plan resulted in an incorrect next call socket, " + nextCall.Socket + ".");

				Console.WriteLine("---------------------------------"); 
			}

			[Test]
			public void SocketWithPortMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:100@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(""anonymous.invalid"", ""password"", ""${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(""anonymous.invalid"", ""password"", ""${dst}@192.168.3.3:5061"")
					exten => 101,1,Switch(""username"", ""password"", ""303@sip.blueface.ie"")
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Username == "anonymous.invalid", "The dial plan resulted in an incorrect next call username, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Password == "password", "The dial plan resulted in an incorrect next call password, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Uri == "sip:100@192.168.3.3:5061", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Assert.IsTrue(nextCall.From == "sip:anonymous.invalid@sip.CNSIPSwitch.com", "The dial plan resulted in an incorrect next call from string, " + nextCall.From + ".");
				Assert.IsTrue(nextCall.To == "sip:100@192.168.3.3:5061", "The dial plan resulted in an incorrect next call to string, " + nextCall.To + ".");
				Assert.IsTrue(nextCall.Socket == "192.168.3.3:5061", "The dial plan resulted in an incorrect next call socket, " + nextCall.Socket + ".");

				Console.WriteLine("---------------------------------"); 
			}

			
			[Test]
			public void RegexMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:100988@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(""anonymous.invalid"", ""password"", ""${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(""anonymous.invalid"", ""password"", ""${dst}@192.168.3.3:5061"")
					exten => 101,1,Switch(""username"", ""password"", ""303@sip.blueface.ie"")
					exten =~ ^100,1,Switch(""user"", ""pass"", ""304@sip.getme.ie:5061"")
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Username == "user", "The dial plan resulted in an incorrect next call username, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Password == "pass", "The dial plan resulted in an incorrect next call password, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Uri == "sip:304@sip.getme.ie:5061", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Assert.IsTrue(nextCall.From == "sip:user@sip.getme.ie:5061", "The dial plan resulted in an incorrect next call from string, " + nextCall.From + ".");
				Assert.IsTrue(nextCall.To == "sip:304@sip.getme.ie:5061", "The dial plan resulted in an incorrect next call to string, " + nextCall.To + ".");
				Assert.IsTrue(nextCall.Socket == "sip.getme.ie:5061", "The dial plan resulted in an incorrect next call socket, " + nextCall.Socket + ".");

				Console.WriteLine("---------------------------------"); 
			}

			[Test]
			public void AstRegexMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:*1100988@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(anonymous.invalid"", password"", ${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(anonymous.invalid"", ""password, ${dst}@192.168.3.3:5061"")
					exten => _*1X.,1,Switch(username,password,${dst:2}@sip.blueface.ie)
					exten =~ ^100,1,Switch(user, pass, 304@sip.getme.ie:5061)
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Username == "username", "The dial plan resulted in an incorrect next call username, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Password == "password", "The dial plan resulted in an incorrect next call password, " + nextCall.Username + ".");
				Assert.IsTrue(nextCall.Uri == "sip:100988@sip.blueface.ie", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Assert.IsTrue(nextCall.From == "sip:username@sip.blueface.ie", "The dial plan resulted in an incorrect next call from string, " + nextCall.From + ".");
				Assert.IsTrue(nextCall.To == "sip:100988@sip.blueface.ie", "The dial plan resulted in an incorrect next call to string, " + nextCall.To + ".");
				Assert.IsTrue(nextCall.Socket == "sip.blueface.ie", "The dial plan resulted in an incorrect next call socket, " + nextCall.Socket + ".");

				Console.WriteLine("---------------------------------"); 
			}

			
			[Test]
			public void AstNoRegexMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:*1000988@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(anonymous.invalid"", password"", ${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(anonymous.invalid"", ""password, ${dst}@192.168.3.3:5061"")
					exten => _*1ZX.,1,Switch(username,password,${dst:2}@sip.blueface.ie)
					exten =~ ^100,1,Switch(user, pass, 304@sip.getme.ie:5061)
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall == SwitchCallStruct.Empty, "The dial plan resulted in a match when it should not have.");
				Console.WriteLine("---------------------------------"); 
			}

			[Test]
			public void AstNoRegexMatchTest2()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:*1110988@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(anonymous.invalid"", password"", ${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(anonymous.invalid"", ""password, ${dst}@192.168.3.3:5061"")
					exten => _*1ZNX.,1,Switch(username,password,${dst:2}@sip.blueface.ie)
					exten =~ ^100,1,Switch(user, pass, 304@sip.getme.ie:5061)
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall == SwitchCallStruct.Empty, "The dial plan resulted in a match when it should not have.");
				Console.WriteLine("---------------------------------"); 
			}

			[Test]
			public void AstNRegexMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:*1120988@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(anonymous.invalid"", password"", ${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(anonymous.invalid"", ""password, ${dst}@192.168.3.3:5061"")
					exten => _*1ZNX.,1,Switch(username,password,${dst:2}@sip.blueface.ie)
					exten =~ ^100,1,Switch(user, pass, 304@sip.getme.ie:5061)
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Uri == "sip:120988@sip.blueface.ie", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Console.WriteLine("---------------------------------"); 
			}

			[Test]
			public void AstReplaceMatchTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:*1120988@sip.blueface.ie");
				testRequest.Header = new SIPHeader();
				testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

				string testDialPlan = @"
					exten => 909,1,Switch(anonymous.invalid"", password"", ${dst}@192.168.3.3:5061"")
					exten => 100,1,Switch(anonymous.invalid"", ""password, ${dst}@192.168.3.3:5061"")
					exten => _*1ZNX.,1,Switch(username,password,${EXTEN:2}@sip.blueface.ie)
					exten =~ ^100,1,Switch(user, pass, 304@sip.getme.ie:5061)
				";

				SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

				SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

				Assert.IsTrue(nextCall.Uri == "sip:120988@sip.blueface.ie", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
				Console.WriteLine("---------------------------------"); 
			}

            [Test]
            public void AstMultiChoiceMatchTest()
            {
                Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

                SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:*1120988@sip.blueface.ie");
                testRequest.Header = new SIPHeader();
                testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

                string testDialPlan = @"
					exten => _Z.,1,Switch(anonymous.invalid, password, 123@192.168.3.3:5061)
					exten => _X.,1,Switch(anonymous.invalid, password, 456@192.168.3.3:5061)
					exten => _*1120X.,1,Switch(username,password,789@sip.blueface.ie)
					exten => _112X.,1,Switch(user, pass, 123@sip.getme.ie:5061)
				";

                SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

                SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

                Console.WriteLine("Match = " + nextCall.Uri.ToString() + ".");

                Assert.IsTrue(nextCall.Uri == "sip:789@sip.blueface.ie", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
                Console.WriteLine("---------------------------------");
            }

            [Test]
            public void AstMultiChoice2MatchTest()
            {
                Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

                SIPRequest testRequest = new SIPRequest(SIPMethodsEnum.INVITE, "sip:1120988@sip.blueface.ie");
                testRequest.Header = new SIPHeader();
                testRequest.Header.From = SIPFromHeader.ParseFromHeader("<sip:joe.bloggs@sip.CNSIPSwitch.com>");

                string testDialPlan = @"
					exten => _1120986,1,Switch(anonymous.invalid, password, 123@192.168.3.3:5061)
					exten => _1120987,1,Switch(anonymous.invalid, password, 456@192.168.3.3:5061)
					exten => _1120988,1,Switch(username,password,789@sip.blueface.ie)
					exten => _1120989,1,Switch(user, pass, 123@sip.getme.ie:5061)
				";

                SIPProxyRuntimeConfig config = new SIPProxyRuntimeConfig(testDialPlan);

                SwitchCallStruct nextCall = config.SwitchCallDialPlan(null, null, testRequest);

                Console.WriteLine("Match = " + nextCall.Uri.ToString() + ".");

                Assert.IsTrue(nextCall.Uri == "sip:789@sip.blueface.ie", "The dial plan resulted in an incorrect next call uri, " + nextCall.Uri + ".");
                Console.WriteLine("---------------------------------");
            }
		}

		#endif

		#endregion
	}
}
